package cn.jjxx.modules.portal.service.impl;

import cn.jjxx.core.model.AjaxJson;
import cn.jjxx.core.utils.IpUtils;
import cn.jjxx.core.utils.JeewebPropertiesUtil;
import cn.jjxx.core.utils.PropertiesUtil;
import cn.jjxx.core.utils.StringUtils;
import cn.jjxx.modules.common.jcaptcha.JCaptcha;
import cn.jjxx.modules.portal.dto.LoginDto;
import cn.jjxx.modules.portal.enums.JCaptchaType;
import cn.jjxx.modules.portal.enums.LoginModeEnum;
import cn.jjxx.modules.portal.service.ILoginService;
import cn.jjxx.modules.portal.utils.MessageConfig;
import cn.jjxx.modules.portal.utils.SMSCodeEhcache;
import cn.jjxx.modules.portal.utils.SMSCodeUtil;
import cn.jjxx.modules.sys.entity.Organization;
import cn.jjxx.modules.sys.entity.User;
import cn.jjxx.modules.sys.security.shiro.exception.RepeatAuthenticationException;
import cn.jjxx.modules.sys.security.shiro.realm.UserRealm;
import cn.jjxx.modules.sys.security.shiro.web.filter.authc.UsernamePasswordToken;
import cn.jjxx.modules.sys.service.IUserService;
import cn.jjxx.modules.sys.utils.UserUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author mali
 */
@Service
public class ILoginServiceImpl implements ILoginService {
    /** 登录时获取验证码最大次数 */
    private static final int MAX_COUNT = Integer.parseInt(JeewebPropertiesUtil.getConfig("maxTakeSMSCodeOfLogin"));
    /** 多少次显示jsp验证码 */
    private static final int SHOW_JCAPTCHA = Integer.parseInt(JeewebPropertiesUtil.getConfig("showJCaptcha"));
    /** 登录时获取验证码计数，根据IP记录 */
    private static final String LOGIN_TAKE_SMS_CACHE_NAME = "loginTakeSMSCount";
    /** 登录获取短信验证码计数器缓存key前缀 */
    private static final String LOGIN_SMS_COUNT_CACHE_PREFIX = "loginTakeSMSCount_";
    /** 登录获取短信验证码缓存名 */
    private static final String LOGIN_TAKE_SMS_CODE_CACHE_NAME = "loginTakeSMSCodeCache";
    /** 登录短信验证码缓存key前缀 */
    private static final String LOGIN_CODE_PREFIX = "loginCode_";

    @Autowired
    private IUserService userService;
    @Autowired
    private CacheManager cacheManager;

    @Override
    public AjaxJson getSMSCode(HttpServletRequest request, String phone, String jcaptchaCode) {
        AjaxJson result = new AjaxJson();
        result.success("操作成功");
        if (StringUtils.isBlank(phone)) {
            result.fail("手机号码不能为空");
            return result;
        }
        User exist = userService.findByPhone(phone);
        if (exist == null) {
            result.fail("该手机号码未注册");
            return result;
        }

        SMSCodeEhcache smsCodeEhcache = new SMSCodeEhcache();
        smsCodeEhcache.setCountCacheName(LOGIN_TAKE_SMS_CACHE_NAME);
        smsCodeEhcache.setCountCacheKey(LOGIN_SMS_COUNT_CACHE_PREFIX + phone);

        SMSCodeUtil.getCount(smsCodeEhcache);
        if (smsCodeEhcache.getCount() > SHOW_JCAPTCHA) {
            if (StringUtils.isBlank(jcaptchaCode)) {
                result.fail(MessageConfig.J_CAPTCHA_NONE);
                return result;
            } else {
                String sessionId = request.getRequestedSessionId();
                String jCaptchaId = sessionId + JCaptchaType.LOGIN_TAKE_SMS_CODE.getCode();
                boolean validateResponse = JCaptcha.validateResponse(request, jCaptchaId, jcaptchaCode);
                if (!validateResponse) {
                    result.fail(MessageConfig.J_CAPTCHA_ERROR);
                    return result;
                }
            }
        }

        smsCodeEhcache.setPhone(phone);
        smsCodeEhcache.setMessageContent(MessageConfig.DEFAULT_CODE_MESSAGE);
        smsCodeEhcache.setMaxCount(MAX_COUNT);
        smsCodeEhcache.setCodeCacheName(LOGIN_TAKE_SMS_CODE_CACHE_NAME);
        smsCodeEhcache.setCodeCacheKey(LOGIN_CODE_PREFIX + phone);
        SMSCodeUtil.sendCode(smsCodeEhcache);
        //result.setData(smsCodeEhcache.getSmsCode());
        result.setData("操作成功");
        return result;
    }

    @Override
    public AjaxJson login(HttpServletRequest request, LoginDto loginDto) {
        AjaxJson result = new AjaxJson();
        result.setRet(AjaxJson.RET_SUCCESS);
        result.setMsg("登录成功!");
        result.put("showCaptcha", false);
        result.put("JSESSIONID", request.getSession().getId());
        String host = IpUtils.getIpAddr(request);

        UserRealm.Principal p = UserUtils.getPrincipal();

        Cache<String, AtomicInteger> passwordRetryCache = cacheManager.getCache("passwordRetryCache");
        AtomicInteger retryCount = passwordRetryCache.get(loginDto.getUsername());
        PropertiesUtil propertiesUtil = new PropertiesUtil("shiro.properties");
        int showCaptcha = Integer.parseInt(propertiesUtil.getString("shiro.user.password.showCaptchaRetryCountOfMobile"));

        // 判断短信验证码是否正确
        if (loginDto.getLoginMode() != null && loginDto.getLoginMode().equals(LoginModeEnum.VCODE.getCode())) {
            SMSCodeEhcache smsCodeEhcache = new SMSCodeEhcache();
            smsCodeEhcache.setCodeCacheName(LOGIN_TAKE_SMS_CODE_CACHE_NAME);
            smsCodeEhcache.setCodeCacheKey(LOGIN_CODE_PREFIX + loginDto.getUsername());
            SMSCodeUtil.getSMSCode(smsCodeEhcache);
            if (!loginDto.getSmsCode().equals(smsCodeEhcache.getSmsCode())) {
                if (retryCount == null) {
                    retryCount = new AtomicInteger(0);
                }
                retryCount.incrementAndGet();
                if (retryCount.get() >= showCaptcha) {
                    result.put("showCaptcha", true);
                }
                passwordRetryCache.put(loginDto.getUsername(), retryCount);
                result.fail(MessageConfig.J_CAPTCHA_ERROR);
                return result;
            }
        }

        try {
            if (p == null) {
                // 验证验证码是否正确
                if (retryCount != null && retryCount.get() >= showCaptcha) {
                    boolean valid = false;
                    if (org.apache.commons.lang3.StringUtils.isNotBlank(loginDto.getJcaptchaCode())) {
                        valid = JCaptcha.validateResponse(request, loginDto.getJcaptchaCode());;
                    }
                    if (valid) {
                        UsernamePasswordToken token = new UsernamePasswordToken(loginDto, host);
                        Subject subject = SecurityUtils.getSubject();
                        subject.login(token);
                        p = UserUtils.getPrincipal();
                    } else {
                        retryCount.incrementAndGet();
                        passwordRetryCache.put(loginDto.getUsername(), retryCount);
                        throw new RepeatAuthenticationException();
                    }
                } else {
                    UsernamePasswordToken token = new UsernamePasswordToken(loginDto, host);
                    Subject subject = SecurityUtils.getSubject();
                    subject.login(token);
                    p = UserUtils.getPrincipal();
                }
            }
        } catch (Exception e) {
            if (e instanceof RepeatAuthenticationException) {
                result.setRet(AjaxJson.RET_FAIL);
                result.put("showCaptcha", true);
                result.fail("验证码错误");
            } else if (e instanceof UnknownAccountException || e instanceof IncorrectCredentialsException) {
                result.setRet(AjaxJson.RET_FAIL);
                result.fail("用户名或密码错误, 请重试。");
                if (retryCount != null && retryCount.get() >= showCaptcha) {
                    result.put("showCaptcha", true);
                }
            } else if (e instanceof LockedAccountException) {
                result.setRet(AjaxJson.RET_FAIL);
                result.fail("帐号被锁定");
            } else if (e instanceof ExcessiveAttemptsException) {
                result.setRet(AjaxJson.RET_FAIL);
                result.fail("请勿重复提交认证，请半小时之后登录。");
            } else {
                result.setRet(AjaxJson.RET_FAIL);
                result.fail("登录失败");
            }
        }
        if (retryCount != null) {
            result.put("retry", retryCount.get());
        }
        return result;
    }
}
